﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;

namespace JobDemo01.Job
{
    public interface IJob
    {
        Guid Id { get; }

        WaferMap Map { get; }

        Task Completion { get; }

        Task<Estimation> Estimation { get; }

        ISubscription<PatternResult> Subscribe();

        void Cancel();

        void Confirm();

        void Block(Task task);

        void ManualAssist(AlignmentHint hint);

        Task MoveTo(int index);

        IAsyncEnumerable<PatternResult> Retry(List<int> indices);
    }

    public class JobRunOptions
    {
        public string OutputDirectory { get; set; }

        public bool SkipAutoFocus { get; set; }

        public bool SkipMeasurement { get; set; }
    }

    class Job(JobContext ctx) : IJob
    {
        public Guid Id => ctx.Id;

        public WaferMap Map => ctx.Map;

        public Task Completion => ctx.Completion;

        public Task<Estimation> Estimation => Task.FromResult<Estimation>(null);

        public ImagingContext ImagingContext { get; } = new(ctx);

        public ISubscription<PatternResult> Subscribe()
        {
            return ctx.MessageQueue.Subscribe();
        }

        public void Block(Task task)
        {
            throw new NotSupportedException();
        }

        public void Cancel()
        {
            ctx.RequestCancellation();
        }

        public void Confirm()
        {
            throw new NotSupportedException();
        }

        public void ManualAssist(AlignmentHint hint)
        {
            throw new NotSupportedException();
        }

        public Task MoveTo(int index)
        {
            throw new NotSupportedException();
        }

        public IAsyncEnumerable<PatternResult> Retry(List<int> indices)
        {
            throw new NotSupportedException();
        }
    }

    class ConfirmationJob(JobContext ctx,
        Task<ExecutionPlan> planTask,
        Func<JobContext, List<int>, Task<ExecutionPlan>, IAsyncEnumerable<PatternResult>> retry) : IJob
    {
        public Guid Id => ctx.Id;

        public WaferMap Map => ctx.Map;

        public Task Completion => ctx.Completion;

        public Task<Estimation> Estimation => AwaitEstimation();

        public ISubscription<PatternResult> Subscribe()
        {
            return ctx.MessageQueue.Subscribe();
        }

        public void Block(Task task)
        {
            ctx.Block(task);
        }

        public void Cancel()
        {
            ctx.RequestCancellation();
        }

        public void Confirm()
        {
            ctx.Confirm();
        }

        public void ManualAssist(AlignmentHint hint)
        {
            ctx.ManualAssist(hint);
        }

        public async Task MoveTo(int index)
        {
            if (retry == null)
            {
                throw new NotSupportedException();
            }

            ctx.Reset();
            await foreach (var _ in retry(ctx, [index], Move(planTask, index)).ConfigureAwait(false))
            {
                // ignored
            }

            async static Task<ExecutionPlan> Move(Task<ExecutionPlan> planTask, int index)
            {
                var plan = await planTask.ConfigureAwait(false);
                return new ExecutionPlan
                {
                    Id = plan.Id,
                    Measurements = plan.Measurements,
                    Map = plan.Map,
                    Environment = plan.Environment,
                    Options = new JobRunOptions
                    {
                        OutputDirectory = plan.Options.OutputDirectory,
                        SkipAutoFocus = true,
                        SkipMeasurement = true,
                    },
                    Alignment = [],
                    ScanPoints = [plan.ScanPoints[index]],
                };
            }
        }

        public IAsyncEnumerable<PatternResult> Retry(List<int> indices)
        {
            if (retry == null)
            {
                throw new NotSupportedException();
            }

            ctx.Reset();
            return retry(ctx, indices, Filter(planTask, indices));

            async static Task<ExecutionPlan> Filter(Task<ExecutionPlan> planTask, List<int> indices)
            {
                var plan = await planTask.ConfigureAwait(false);
                var points = new List<ScanPoint>();
                foreach (var i in indices)
                {
                    points.Add(plan.ScanPoints[i]);
                }
                return new ExecutionPlan
                {
                    Id = plan.Id,
                    Measurements = plan.Measurements,
                    Map = plan.Map,
                    Environment = plan.Environment,
                    Options = plan.Options,
                    Alignment = [],
                    ScanPoints = points,
                };
            }
        }

        async Task<Estimation> AwaitEstimation()
        {
            var plan = await planTask.ConfigureAwait(false);
            return plan.Estimation;
        }
    }
}
